Quiz (Week 8)
Table of Contents
1 IO Monad i
1.1 Question 1
What does the type IO String signify?
- ✗A procedure that, when carried out, may involve input and output, before eventually returning a
String. - ✗A function from the abstract state of the
RealWorldto a pair of theRealWorldstate and anString. - ✗An effectful program that, when executed, produces an
String. - ✔All of the above views are valid interpretations
GHC internally models IO a similarly to the following type:
IO a =~ RealWorld -> (RealWorld, a)
This is a common view of IO and option 2. Perhaps an even more common view of IO is that
it denotes procedure. For example,
getChar :: IO Char
could be viewed as a type representing a procedure that will produce a Char
when it's carried out. This is a very intuitive way of thinking about IO: a function of
type String -> IO (), such as putStrLn, takes a String and returns a procedure
that, when carried out, results in the given string being printed. Keep in mind that the
procedure is not carried out inside the Haskell program: rather, it's carried out when
the program is executed.
1.2 Question 2
Imagine we had the following IO based API for manipulating a robot:
data Direction = L | R forward :: IO () obstructed :: IO Bool turn :: Direction -> IO ()
We wish to write a program that will move forward unless obstructed, in which case
the robot should turn towards the L direction.
Which of the following is a type-correct implementation of the above procedure?
- ✔
robot = do sensed <- obstructed if sensed then turn L else forward robot
- ✗
robot = do if obstructed then turn L else forward robot
- ✗
robot = do let sensed = obstructed if sensed then turn L else forward robot
- ✗
robot = do sensed <- obstructed if sensed then turn L robot else forward robot
Option 1 is correct. Option 2 uses an IO Bool (obstructed) where a Bool is required (in the if).
Option 3 uses let to bind sensed to obstructed. That is, sensed now has type IO Bool, which
once again is incorrectly used within the if. Option 4 places the robot looping call at the same indentation
as turn L and forward, but without the do keyword they do not form a block and so Haskell would
parse this as turn L robot which is not well-typed.
1.3 Question 3
Check all of the following programs that are equivalent to the IO action b:
b = do x <- getLine putStrLn (filter (not . isDigit) x) b
- ✔
a = getLine >>= putStrLn . filter (not . isDigit) >> a - ✔
a = getLine >>= \x -> putStrLn (filter (not . isDigit) x) >>= \_ -> a - ✗
a = (getLine >>= \x -> putStrLn (filter (not . isDigit) x)) >>= a - ✗
a = do getLine >>= \x -> putStrLn . filter (not . isDigit); a - ✗
a = do x <- getLine; putStrLn . filter (not . isDigit); a - ✔
a = do x <- getLine; putStrLn . filter (not . isDigit) $ x; a - ✔
a = do getLine >>= \x -> putStrLn (filter (not . isDigit) x); a
Options 3,4, and 5 don't type-check. The others are all equivalent.
1.4 Question 4
Which of the following defines a function that takes a String as its argument and
causes it to be written to the standard output, given that putChar :: Char -> IO ()
takes a character as its parameter and writes it to the standard output.
- ✗
f [] = return "" f (x:xs) = putChar x >>= \_ -> xs
- ✔
f [] = return () f (x:xs) = putChar x >>= \_ -> f xs
- ✗
f [] = return () f (x:xs) = putChar x >>= \xs -> f (show xs)
- ✗
f xs = case xs of [] -> return () (x:xs) -> f xs >>= \_ -> putChar x
The first and fourth are not even type-correct. The third is, but it's not terminating. The second implementation is correct.
1.5 Question 5
Pick all possible implementations that define a function
mapIO :: (a -> b) -> IO a -> IO b that takes a function of
type a -> b and "maps" it over a non-bottom monadic value of type
IO a to produce a value of type IO b?
- ✗
mapIO f m = return (f m)
- ✔
mapIO f m = m >>= \x -> return (f x)
- ✗
mapIO f m = m >>= \x -> f x
- ✔
mapIO f = (f <$>)
The first and third answers do not type-check. In the second implementation,
we have that m has type IO a, so the variable x has type a. Therefore,
the variable f x has type b, and return (f x) has type IO b as desired.
The fourth answer works by using the fact that all Monad~s are ~Applicative.
1.6 Question 6
Which of the following types can a total/non-partial terminating function never have?
- ✗
a :: IO (a -> b) -> IO a -> IO b - ✗
b :: IO (a -> b) -> a -> IO b - ✔
c :: IO (a -> b) -> (a -> b) - ✗
d :: a -> IO a
The first function, a is just a = fmap, using the fact that every Monad, including IO, is a Functor.
Similarly, the third function, d is just d = return.
The second function can be implemented as follows:
b :: IO (a -> b) -> a -> IO b b f x = f >>= \f' -> return (f' x)
This leaves the third function, which cannot be implemented: if we had such an implementation c,
we could use that c to also implement the following function:
evalIO :: IO a -> a evalIO p = (p >>= \x -> c (return (const x))) ()
but we know from the lecture that evaluating IO like this is impossible.
2 List Comprehensions
2.1 Question 7
Which of the following expressions evaluates to the sum of the first 100 square numbers?
- ✗
foldl (++) [] [[x * x] | x <- [1..100]] - ✔
sum [x^2 | x <- [1..100]] - ✗
sum [const x x | x <- [1..100]] - ✔
sum (map (^2) [1..100])
The first expression does not even type-check. The second is correct, as is the fourth.
The third one calculates the sum of the first 100 integers, since const x x == x.
#+ENDSRC
2.2 Question 8
Choose the expression equivalent to the list comprehension [f x | x <- xs, phi x] !
- ✗
map f (map phi xs) - ✗
filter phi (map f xs) - ✔
map f (filter phi xs) - ✗
map phi (filter f xs)
Only the third choice is type-correct.